解构赋值与其他 ES6 特性面试题
一、核心要点速览
💡 核心考点
- 解构赋值: 从数组/对象中提取数据
- 模板字符串: 多行字符串与插值语法
- 剩余参数: ...args 收集剩余参数
- 扩展运算符: ...array 展开数组/对象
- Symbol: 唯一标识符
二、数组解构
1. 基础用法
javascript
// 基本解构
const [a, b, c] = [1, 2, 3]
console.log(a) // 1
console.log(b) // 2
console.log(c) // 3
// 跳过某些值
const [first, , third] = [1, 2, 3]
console.log(first) // 1
console.log(third) // 3
// 默认值
const [x = 1, y = 2] = [undefined]
console.log(x) // 1(默认值生效)
console.log(y) // 2(默认值生效)
// 只在值为 undefined 时使用默认值
const [a = 1, b = 2] = [null, 0]
console.log(a) // null(不是 undefined)
console.log(b) // 0(不是 undefined)2. 剩余运算符
javascript
// 收集剩余元素
const [head, ...tail] = [1, 2, 3, 4]
console.log(head) // 1
console.log(tail) // [2, 3, 4]
// 交换变量
let a = 1, b = 2
[a, b] = [b, a]
console.log(a) // 2
console.log(b) // 1
// 嵌套解构
const [1, [2, 3]] = [1, [2, 3]]
const [x, [y, z]] = [1, [2, 3]]
console.log(x) // 1
console.log(y) // 2
console.log(z) // 33. 函数参数解构
javascript
// 数组参数解构
function printPair([first, second]) {
console.log(first, second)
}
printPair([1, 2]) // 1 2
// 带默认值
function multiply([a = 1, b = 1] = []) {
return a * b
}
multiply() // 1(使用默认参数和默认值)
multiply([2, 3]) // 6
multiply([5]) // 5三、对象解构
1. 基础用法
javascript
// 基本解构
const { name, age } = { name: 'Vue', age: 3 }
console.log(name) // 'Vue'
console.log(age) // 3
// 重命名
const { name: userName, age: userAge } = { name: 'Vue', age: 3 }
console.log(userName) // 'Vue'
console.log(userAge) // 3
// 默认值
const { count = 0, enabled = true } = {}
console.log(count) // 0
console.log(enabled) // true
// 先声明后解构
let x, y
;({ x, y } = { x: 1, y: 2 }) // 注意括号2. 嵌套解构
javascript
// 嵌套对象
const {
user: {
name,
profile: { email }
}
} = {
user: {
name: 'Vue',
profile: {
email: '[email protected]'
}
}
}
console.log(name) // 'Vue'
console.log(email) // '[email protected]'
// 设置默认值
const {
user: { name = 'Anonymous' } = {}
} = {}
console.log(name) // 'Anonymous'3. 函数参数解构
javascript
// 对象参数解构
function printUser({ name, age }) {
console.log(`${name}, ${age}`)
}
printUser({ name: 'Vue', age: 3 }) // Vue, 3
// 重命名 + 默认值
function createUser({
name = 'Guest',
email = '[email protected]',
role = 'user'
} = {}) {
return { name, email, role }
}
createUser() // { name: 'Guest', email: '[email protected]', role: 'user' }
// 实际应用:配置对象
function fetch(url, {
method = 'GET',
headers = {},
timeout = 5000,
retry = 3
} = {}) {
// ...
}四、实际应用场景
1. 函数返回多个值
javascript
// 返回对象
function getStats() {
return {
success: 200,
failed: 5,
total: 205
}
}
const { success, failed, total } = getStats()
// 返回数组
function getMinMax(arr) {
return [Math.min(...arr), Math.max(...arr)]
}
const [min, max] = getMinMax([3, 1, 4, 1, 5])
console.log(min) // 1
console.log(max) // 52. 遍历 Map
javascript
const map = new Map([
['a', 1],
['b', 2],
['c', 3]
])
// 解构键值对
for (const [key, value] of map) {
console.log(key, value)
}
// forEach 中也可以
map.forEach((value, key) => {
console.log(key, value)
})3. 提取 JSON 数据
javascript
// 从 API 响应中提取
async function getUser(id) {
const response = await fetch(`/api/users/${id}`)
const { data: { name, email }, meta: { timestamp } } = await response.json()
return { name, email, timestamp }
}
// 简化写法
async function getUser(id) {
const { data: { name, email } } = await fetch(`/api/users/${id}`).then(r => r.json())
return { name, email }
}五、模板字符串
1. 基础用法
javascript
const name = 'Vue'
const version = 3
// 插值
const greeting = `Hello, ${name}!`
console.log(greeting) // Hello, Vue!
// 表达式
const result = `结果:${1 + 2} = ${3}`
// 多行字符串
const html = `
<div class="container">
<h1>${name}</h1>
<p>Version ${version}</p>
</div>
`
// 普通字符串对比
const multiLine = '第一行\n' +
'第二行\n' +
'第三行'
const multiLineES6 = `第一行
第二行
第三行`2. 标签模板
javascript
// 标签函数
function tag(strings, ...values) {
console.log(strings) // ['Hello ', '']
console.log(values) // ['World']
return strings[0] + values[0].toUpperCase() + strings[1]
}
const result = tag`Hello ${'World'}!`
console.log(result) // Hello WORLD!
// 实际应用:转义 HTML
function escapeHtml(strings, ...values) {
const escape = str => str.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
let result = ''
values.forEach((val, i) => {
result += strings[i] + escape(String(val))
})
result += strings[strings.length - 1]
return result
}
const userInput = '<script>alert("XSS")</script>'
const safe = escapeHtml`<div>${userInput}</div>`
// <div><script>alert("XSS")</script></div>3. String.raw
javascript
// 获取原始字符串(不处理转义)
const path = String.raw`C:\Users\Name\Documents`
console.log(path) // C:\Users\Name\Documents
// 对比
const normal = `C:\\Users\\Name\\Documents`
console.log(normal) // C:\Users\Name\Documents六、剩余参数与扩展运算符
1. 剩余参数 (...args)
javascript
// 函数参数
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0)
}
sum(1, 2, 3, 4) // 10
// 必须放在最后
function fn(required, optional = 1, ...rest) {
console.log(required) // 必需参数
console.log(optional) // 可选参数
console.log(rest) // 剩余参数
}
fn(1, 2, 3, 4, 5) // 1, 2, [3, 4, 5]
// 解构中的剩余参数
const [first, second, ...others] = [1, 2, 3, 4, 5]
console.log(first) // 1
console.log(second) // 2
console.log(others) // [3, 4, 5]2. 扩展运算符 (...spread)
javascript
// 数组展开
const arr1 = [1, 2]
const arr2 = [...arr1, 3, 4]
console.log(arr2) // [1, 2, 3, 4]
// 数组合并
const merged = [...arr1, ...arr2]
console.log(merged) // [1, 2, 1, 2, 3, 4]
// 复制数组
const copy = [...arr1]
console.log(copy) // [1, 2]
// 对象展开
const obj1 = { a: 1, b: 2 }
const obj2 = { ...obj1, c: 3 }
console.log(obj2) // { a: 1, b: 2, c: 3 }
// 对象合并
const merged = { ...obj1, ...obj2 }
console.log(merged) // { a: 1, b: 2, c: 3 }
// 注意:后面的属性覆盖前面的
const override = { ...obj1, b: 99 }
console.log(override) // { a: 1, b: 99 }3. 实际应用
javascript
// 不可变更新
function updateUser(user, updates) {
return { ...user, ...updates }
}
const user = { name: 'Vue', age: 3 }
const updated = updateUser(user, { age: 4 })
console.log(updated) // { name: 'Vue', age: 4 }
// 数组转 Set(去重)
const unique = [...new Set([1, 2, 2, 3, 3, 3])]
console.log(unique) // [1, 2, 3]
// Set 转数组
const arr = [...new Set([1, 2, 3])]
// 字符串转数组
const chars = [...'Hello']
console.log(chars) // ['H', 'e', 'l', 'l', 'o']七、Symbol
1. 创建与特性
javascript
// 创建唯一值
const id1 = Symbol('id')
const id2 = Symbol('id')
console.log(id1 === id2) // false
// Symbol 总是唯一的,即使描述相同
const sym1 = Symbol()
const sym2 = Symbol()
console.log(typeof sym1) // 'symbol'
console.log(sym1.toString()) // 'Symbol()'2. 作为对象属性
javascript
// 隐藏属性
const obj = {
[Symbol('id')]: 1,
name: 'Vue'
}
console.log(obj.name) // 'Vue'
console.log(Object.keys(obj)) // ['name'](不包含 Symbol)
console.log(Object.getOwnPropertySymbols(obj)) // [Symbol(id)]
// 访问 Symbol 属性
const symbols = Object.getOwnPropertySymbols(obj)
console.log(obj[symbols[0]]) // 13. 全局共享 Symbol
javascript
// 全局注册
const shared1 = Symbol.for('shared')
const shared2 = Symbol.for('shared')
console.log(shared1 === shared2) // true
// 获取描述
const key = Symbol.keyFor(shared1)
console.log(key) // 'shared'
// 应用:Well-Known Symbols
const iterable = {
[Symbol.iterator]() {
let step = 0
return {
next() {
if (step < 3) {
return { value: step++, done: false }
}
return { value: undefined, done: true }
}
}
}
}
for (const item of iterable) {
console.log(item) // 0, 1, 2
}八、面试标准回答
解构赋值是 ES6 引入的语法特性,允许从数组或对象中提取数据并赋值给变量。
数组解构按照位置对应,可以跳过某些值、设置默认值、使用剩余运算符收集剩余元素。还支持嵌套解构和交换变量。
对象解构按照属性名匹配,可以重命名、设置默认值。在函数参数中非常有用,可以替代配置对象模式。
实际应用场景包括:
- 函数返回多个值
- 遍历 Map 时的键值对提取
- 从 API 响应中提取嵌套数据
- 函数参数的简化
模板字符串提供了更强大的字符串功能:
- 多行字符串无需拼接
- 插值语法
${expression}- 标签模板实现自定义处理
剩余参数和扩展运算符是两个重要特性:
- 剩余参数(...args)收集剩余元素
- 扩展运算符(...array)展开数组或对象
- 支持不可变数据更新
Symbol 是唯一标识符,主要用于:
- 创建隐藏属性
- 避免属性名冲突
- 实现迭代器协议
实际项目中,我大量使用解构赋值简化代码,用模板字符串处理文本,用扩展运算符实现不可变更新。
九、记忆口诀
解构赋值歌诀:
数组解构按位置,
对象解构按名称。
默认值来保底,
剩余参数收末尾!
模板字符串强大,
多行插值都用它。
标签模板能定制,
String.raw 保原样!
剩余扩展是一对,
一个收集一个展。
Symbol 是唯一,
隐藏属性防冲突!十、推荐资源
十一、总结一句话
- 解构赋值: 模式匹配 + 数据提取 = 简洁代码利器 ✨
- 模板字符串: 多行文本 + 表达式插值 = 字符串处理神器 📝
- 剩余扩展: 收集剩余 + 展开数组 = 函数参数必备 🎯
- Symbol: 唯一标识 + 隐藏属性 = 避免命名冲突 🔐